{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# KNN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "魔数:2051, 图片数量: 60000张, 图片大小: 28*28\n",
      "16\n",
      ">784B 16 784\n",
      "已解析 10000张\n",
      "7839232\n",
      "已解析 20000张\n",
      "15679232\n",
      "已解析 30000张\n",
      "23519232\n",
      "已解析 40000张\n",
      "31359232\n",
      "已解析 50000张\n",
      "39199232\n",
      "已解析 60000张\n",
      "47039232\n",
      "魔数:2049, 图片数量: 60000张\n",
      "已解析 10000张\n",
      "已解析 20000张\n",
      "已解析 30000张\n",
      "已解析 40000张\n",
      "已解析 50000张\n",
      "已解析 60000张\n",
      "5.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "done\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import struct\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 训练集文件\n",
    "train_images_idx3_ubyte_file = r\"C:\\Users\\39692\\Desktop\\data\\Python\\第九周机器学习\\第九周机器学习上课程资料\\4.分类训练\\代码\\MNIST_data\\train-images-idx3-ubyte\"\n",
    "# 训练集标签文件\n",
    "train_labels_idx1_ubyte_file = r\"C:\\Users\\39692\\Desktop\\data\\Python\\第九周机器学习\\第九周机器学习上课程资料\\4.分类训练\\代码\\MNIST_data\\train-labels-idx1-ubyte\"\n",
    "\n",
    "# 测试集文件\n",
    "test_images_idx3_ubyte_file = r\"C:\\Users\\39692\\Desktop\\data\\Python\\第九周机器学习\\第九周机器学习上课程资料\\4.分类训练\\代码\\MNIST_data\\t10k-images-idx3-ubyte\"\n",
    "# 测试集标签文件\n",
    "test_labels_idx1_ubyte_file = r\"C:\\Users\\39692\\Desktop\\data\\Python\\第九周机器学习\\第九周机器学习上课程资料\\4.分类训练\\代码\\MNIST_data\\t10k-labels-idx1-ubyte\"\n",
    "\n",
    "\n",
    "def decode_idx3_ubyte(idx3_ubyte_file):\n",
    "    \"\"\"\n",
    "    解析idx3文件的通用函数\n",
    "    :param idx3_ubyte_file: idx3文件路径\n",
    "    :return: 数据集\n",
    "    \"\"\"\n",
    "    # 读取二进制数据\n",
    "    bin_data = open(idx3_ubyte_file, 'rb').read()\n",
    "\n",
    "    # 解析文件头信息，依次为魔数、图片数量、每张图片高、每张图片宽\n",
    "    offset = 0\n",
    "    fmt_header = '>iiii' #因为数据结构中前4行的数据类型都是32位整型，所以采用i格式，但我们需要读取前4行数据，所以需要4个i。我们后面会看到标签集中，只使用2个ii。\n",
    "    magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, offset)\n",
    "    print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))\n",
    "\n",
    "    # 解析数据集\n",
    "    image_size = num_rows * num_cols\n",
    "    offset += struct.calcsize(fmt_header)  #获得数据在缓存中的指针位置，从前面介绍的数据结构可以看出，读取了前4行之后，指针位置（即偏移位置offset）指向0016。\n",
    "    print(offset)\n",
    "    fmt_image = '>' + str(image_size) + 'B'  #图像数据像素值的类型为unsigned char型，对应的format格式为B。这里还有加上图像大小784，是为了读取784个B格式数据，如果没有则只会读取一个值（即一副图像中的一个像素值）\n",
    "    print(fmt_image,offset,struct.calcsize(fmt_image))\n",
    "    images = np.empty((num_images, num_rows, num_cols))\n",
    "    #plt.figure()\n",
    "    for i in range(num_images):\n",
    "        if (i + 1) % 10000 == 0:\n",
    "            print('已解析 %d' % (i + 1) + '张')\n",
    "            print(offset)\n",
    "        images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))\n",
    "        #print(images[i])\n",
    "        offset += struct.calcsize(fmt_image)\n",
    "#        plt.imshow(images[i],'gray')\n",
    "#        plt.pause(0.00001)\n",
    "#        plt.show()\n",
    "    #plt.show()\n",
    "\n",
    "    return images\n",
    "\n",
    "\n",
    "def decode_idx1_ubyte(idx1_ubyte_file):\n",
    "    \"\"\"\n",
    "    解析idx1文件的通用函数\n",
    "    :param idx1_ubyte_file: idx1文件路径\n",
    "    :return: 数据集\n",
    "    \"\"\"\n",
    "    # 读取二进制数据\n",
    "    bin_data = open(idx1_ubyte_file, 'rb').read()\n",
    "\n",
    "    # 解析文件头信息，依次为魔数和标签数\n",
    "    offset = 0\n",
    "    fmt_header = '>ii'\n",
    "    magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)\n",
    "    print('魔数:%d, 图片数量: %d张' % (magic_number, num_images))\n",
    "\n",
    "    # 解析数据集\n",
    "    offset += struct.calcsize(fmt_header)\n",
    "    fmt_image = '>B'\n",
    "    labels = np.empty(num_images)\n",
    "    for i in range(num_images):\n",
    "        if (i + 1) % 10000 == 0:\n",
    "            print ('已解析 %d' % (i + 1) + '张')\n",
    "        labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]\n",
    "        offset += struct.calcsize(fmt_image)\n",
    "    return labels\n",
    "\n",
    "\n",
    "def load_train_images(idx_ubyte_file=train_images_idx3_ubyte_file):\n",
    "    \"\"\"\n",
    "    TRAINING SET IMAGE FILE (train-images-idx3-ubyte):\n",
    "    [offset] [type]          [value]          [description]\n",
    "    0000     32 bit integer  0x00000803(2051) magic number\n",
    "    0004     32 bit integer  60000            number of images\n",
    "    0008     32 bit integer  28               number of rows\n",
    "    0012     32 bit integer  28               number of columns\n",
    "    0016     unsigned byte   ??               pixel\n",
    "    0017     unsigned byte   ??               pixel\n",
    "    ........\n",
    "    xxxx     unsigned byte   ??               pixel\n",
    "    Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).\n",
    "\n",
    "    :param idx_ubyte_file: idx文件路径\n",
    "    :return: n*row*col维np.array对象，n为图片数量\n",
    "    \"\"\"\n",
    "    return decode_idx3_ubyte(idx_ubyte_file)\n",
    "\n",
    "\n",
    "def load_train_labels(idx_ubyte_file=train_labels_idx1_ubyte_file):\n",
    "    \"\"\"\n",
    "    TRAINING SET LABEL FILE (train-labels-idx1-ubyte):\n",
    "    [offset] [type]          [value]          [description]\n",
    "    0000     32 bit integer  0x00000801(2049) magic number (MSB first)\n",
    "    0004     32 bit integer  60000            number of items\n",
    "    0008     unsigned byte   ??               label\n",
    "    0009     unsigned byte   ??               label\n",
    "    ........\n",
    "    xxxx     unsigned byte   ??               label\n",
    "    The labels values are 0 to 9.\n",
    "\n",
    "    :param idx_ubyte_file: idx文件路径\n",
    "    :return: n*1维np.array对象，n为图片数量\n",
    "    \"\"\"\n",
    "    return decode_idx1_ubyte(idx_ubyte_file)\n",
    "\n",
    "\n",
    "def load_test_images(idx_ubyte_file=test_images_idx3_ubyte_file):\n",
    "    \"\"\"\n",
    "    TEST SET IMAGE FILE (t10k-images-idx3-ubyte):\n",
    "    [offset] [type]          [value]          [description]\n",
    "    0000     32 bit integer  0x00000803(2051) magic number\n",
    "    0004     32 bit integer  10000            number of images\n",
    "    0008     32 bit integer  28               number of rows\n",
    "    0012     32 bit integer  28               number of columns\n",
    "    0016     unsigned byte   ??               pixel\n",
    "    0017     unsigned byte   ??               pixel\n",
    "    ........\n",
    "    xxxx     unsigned byte   ??               pixel\n",
    "    Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).\n",
    "\n",
    "    :param idx_ubyte_file: idx文件路径\n",
    "    :return: n*row*col维np.array对象，n为图片数量\n",
    "    \"\"\"\n",
    "    return decode_idx3_ubyte(idx_ubyte_file)\n",
    "\n",
    "\n",
    "def load_test_labels(idx_ubyte_file=test_labels_idx1_ubyte_file):\n",
    "    \"\"\"\n",
    "    TEST SET LABEL FILE (t10k-labels-idx1-ubyte):\n",
    "    [offset] [type]          [value]          [description]\n",
    "    0000     32 bit integer  0x00000801(2049) magic number (MSB first)\n",
    "    0004     32 bit integer  10000            number of items\n",
    "    0008     unsigned byte   ??               label\n",
    "    0009     unsigned byte   ??               label\n",
    "    ........\n",
    "    xxxx     unsigned byte   ??               label\n",
    "    The labels values are 0 to 9.\n",
    "\n",
    "    :param idx_ubyte_file: idx文件路径\n",
    "    :return: n*1维np.array对象，n为图片数量\n",
    "    \"\"\"\n",
    "    return decode_idx1_ubyte(idx_ubyte_file)\n",
    "\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    train_images = load_train_images()\n",
    "\n",
    "    train_labels = load_train_labels()\n",
    "    # test_images = load_test_images()\n",
    "    # test_labels = load_test_labels()\n",
    "\n",
    "    # 查看前十个数据及其标签以读取是否正确\n",
    "    for i in range(10):\n",
    "        print(train_labels[i])\n",
    "        plt.imshow(train_images[i], cmap='gray')\n",
    "        plt.pause(0.000001)\n",
    "        plt.show()\n",
    "    print('done')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = train_images\n",
    "y = train_labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) # 为了看模型在没有见过数据集上的表现，随机拿出数据集中30%的部分做测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "KNeighborsClassifier()"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn import neighbors\n",
    "knn = neighbors.KNeighborsClassifier(n_neighbors=5)  \n",
    "#训练数据集  \n",
    "knn.fit(X_train.reshape(42000,-1), y_train.ravel())  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_pred = knn.predict(X_test.reshape(18000,-1))\n",
    "\n",
    "print(\"精度: {:.2f}%\".format(100 * precision_score(y_train, y_pred)))\n",
    "print(\"召回: {:.2f}%\".format(100 * recall_score(y_train, y_pred)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
